Skip to content

Conversation

@georgewrmarshall
Copy link
Contributor

@georgewrmarshall georgewrmarshall commented Oct 18, 2025

Description

This PR implements the BalanceEmptyState component on the homepage when users have a zero balance across all mainnet networks. It is hidden behind the homepageRedesignV1Key feature flag which will be used for full page scroll of the homepage.

  • Shows BalanceEmptyState component for accounts with an aggregated balance of zero across mainnets(excludes testnets)
  • Applies to only AccountGroupBalance component.
  • Replaces the regular balance display with an empty state component when total aggregated balance is zero
  • Replaces previous TokenListFooter(Removed in this PR: chore: Remove TokenListFooter component in favor of balance empty state #21490)
  • Hidden behind the homepageRedesignV1Key feature flag
  • Adds new selectWalletBalanceForEmptyState to get balance across all networks
  • Adds e2e tests

Changelog

CHANGELOG entry: Updating zero balance display to "Add funds" card

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-153

Manual testing steps

Feature: Balance Empty State on Homepage

  Background:
    Given the homepageRedesignV1Key feature flag is enabled

  Scenario: User with zero balance across all aggregated networks sees empty state
    Given user has a wallet with zero balance across all aggregated networks (Evm and nonEvm)
    When user navigates to the homepage
    Then user should see the BalanceEmptyState component with illustration and "Add funds" button
    And user should not see the regular balance display showing "$0.00"

  Scenario: User with positive balance on all aggregated networks sees positive balance
    Given user has a wallet with positive balance across aggregated networks (EVM and non EVM)
    When user navigates to the homepage
    Then user should see the normal balance display showing the actual dollar amount
    And user should not see the BalanceEmptyState component

  Scenario: User changes to network with empty balance sees zero balance
    Given user has positive balance on some networks but zero on others
    When user switches to a network where they have zero balance (EVM and non EVM)
    Then user should see "$0.00" balance display

  Scenario: User changes to network with positive balance sees positive balance (including test networks)
    Given user has zero balance on some networks but positive on others
    When user switches to a network where they have positive balance (mainnet, Sepolia, Goerli, etc.)
    Then user should see the normal balance display with actual amount
    And user should not see the BalanceEmptyState component
    And this behavior should be consistent across mainnet and test networks

  Scenario: User taps "Add funds" button from empty state
    Given user sees the BalanceEmptyState on homepage
    When user taps the "Add funds" button
    Then user should be navigated to the buy crypto page (not deposit flow)
    And appropriate analytics events should be tracked

  Scenario: Feature flag disabled - no empty state shown
    Given the homepageRedesignV1Key feature flag is disabled
    And user has zero balance
    When user navigates to the homepage
    Then user should see the regular "$0.00" balance display
    And user should not see the BalanceEmptyState component

  Scenario: Privacy mode interaction with empty state
    Given user has zero balance and sees BalanceEmptyState
    When user toggles privacy mode
    Then the BalanceEmptyState should remain visible (not affected by privacy toggle)

  Scenario: Loading states
    Given user navigates to homepage
    When balance data is still loading
    Then user should see skeleton/loader components
    And user should not see BalanceEmptyState or balance display until data loads

Screenshots/Recordings

Before

Empty balance showed a 0 balance and footer CTA to go to deposit

before720.mov

Loading balance on first import of an account with zero balance

loadingbefore720.mov

After

Empty balance now shows BalanceEmptyState component

after720.mov

The flicker between a zero balance(now empty state) still exists and is an architectural issue outside of the scope of this PR. This is being worked on by the @MetaMask/metamask-assets team

loadingafter720.mov

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or recordings and or screenshots.

Note

Show BalanceEmptyState in AccountGroupBalance when aggregated mainnet balance is zero (feature-flagged), with new selector and comprehensive tests.

  • UI (Wallet):
    • Display BalanceEmptyState in AccountGroupBalance when aggregated mainnet balance is zero; hide on testnets; keep skeleton while loading; gated by selectHomepageRedesignV1Enabled.
    • Uses TEST_NETWORK_IDS and selectEvmChainId to suppress empty state on test networks.
  • Selectors:
    • Add selectAccountGroupBalanceForEmptyState to compute selected group balance across mainnet networks only, using CAIP utilities and excluding TEST_NETWORK_IDS and NON_EVM_TESTNET_IDS.
  • Tests:
    • Unit: update AccountGroupBalance.test.tsx to cover empty state rendering; add selector tests for network filtering, no selection, and missing group fallbacks.
    • E2E: add balance-empty-state.spec.ts validating visibility on mainnet vs testnets, navigation to buy flow, and persistence after restart; extend page object WalletView and selectors with BALANCE_EMPTY_STATE_* IDs.

Written by Cursor Bugbot for commit 2078f33. This will update automatically on new commits. Configure here.

@github-actions
Copy link
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbot metamaskbot added the team-design-system All issues relating to design system in Mobile label Oct 18, 2025
@georgewrmarshall georgewrmarshall changed the title feat: Implement BalanceEmptyState component on homepage for zero balance users feat: implement BalanceEmptyState component on homepage for zero balance users Oct 19, 2025
@georgewrmarshall georgewrmarshall added needs-qa Any New Features that needs a full manual QA prior to being added to a release. Run E2E Smoke Test labels Oct 19, 2025
@georgewrmarshall georgewrmarshall self-assigned this Oct 19, 2025
@georgewrmarshall georgewrmarshall force-pushed the feat/balance-empty-state branch from 1403362 to 874da16 Compare October 21, 2025 19:26
@github-actions github-actions bot added size-M and removed size-L labels Oct 21, 2025
@georgewrmarshall georgewrmarshall changed the title feat: implement BalanceEmptyState component on homepage for zero balance users feat: implement BalanceEmptyState component Oct 21, 2025
@georgewrmarshall georgewrmarshall force-pushed the feat/balance-empty-state branch from 874da16 to 88f746b Compare October 21, 2025 23:34
@georgewrmarshall georgewrmarshall force-pushed the feat/balance-empty-state branch 2 times, most recently from 51cc193 to 33d1845 Compare October 24, 2025 19:00
@codecov-commenter
Copy link

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.09%. Comparing base (9c85997) to head (f1ac442).
⚠️ Report is 11 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #21391      +/-   ##
==========================================
+ Coverage   76.98%   77.09%   +0.11%     
==========================================
  Files        3715     3719       +4     
  Lines       93270    93446     +176     
  Branches    17894    17929      +35     
==========================================
+ Hits        71801    72046     +245     
+ Misses      16580    16508      -72     
- Partials     4889     4892       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@georgewrmarshall georgewrmarshall force-pushed the feat/balance-empty-state branch from f1ac442 to 175af1f Compare October 24, 2025 20:31
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements the BalanceEmptyState component on the homepage when users have a zero balance across all networks, replacing the default zero balance display with an engaging card featuring a "Fund your wallet" illustration and "Add funds" button to improve the onboarding experience for new users.

Key changes:

  • Integrated BalanceEmptyState into both PortfolioBalance (legacy multichain) and AccountGroupBalance (multichain accounts state 2) components
  • Replaced ButtonHero with standard Button component in BalanceEmptyState
  • Removed TokenListFooter from token list display

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
app/components/UI/Tokens/TokenList/index.tsx Removed TokenListFooter component from list display
app/components/UI/Tokens/TokenList/PortfolioBalance/index.tsx Integrated BalanceEmptyState for zero balance display
app/components/UI/Tokens/TokenList/PortfolioBalance/index.test.tsx Added comprehensive test coverage for empty state scenarios
app/components/UI/BalanceEmptyState/BalanceEmptyState.tsx Replaced ButtonHero with standard Button component
app/components/UI/Assets/components/Balance/AccountGroupBalance.tsx Integrated BalanceEmptyState for zero balance display
app/components/UI/Assets/components/Balance/AccountGroupBalance.test.tsx Added test coverage for empty state scenarios

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +31 to +39
const isHomepageRedesignV1Enabled = useSelector(
selectHomepageRedesignV1Enabled,
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using new feature flag to conditionally render the balance empty state

@georgewrmarshall georgewrmarshall marked this pull request as ready for review October 27, 2025 19:56
@georgewrmarshall georgewrmarshall requested review from a team as code owners October 27, 2025 19:56
@georgewrmarshall georgewrmarshall force-pushed the feat/balance-empty-state branch from 172814e to d6a61a8 Compare October 27, 2025 21:01
@georgewrmarshall georgewrmarshall force-pushed the feat/balance-empty-state branch from 4115e16 to 2078f33 Compare October 31, 2025 05:06
@github-actions github-actions bot added size-L and removed size-M labels Oct 31, 2025
Comment on lines +12 to 44
// This selector is used to display the BalanceEmptyState
selectAccountGroupBalanceForEmptyState: jest.fn(() => null),
}));

// Mock homepage redesign feature flag for BalanceEmptyState
jest.mock('../../../../../selectors/featureFlagController/homepage', () => ({
selectHomepageRedesignV1Enabled: jest.fn(() => true),
}));

// This selector is used to determine if the current network is a testnet for BalanceEmptyState display logic
jest.mock('../../../../../selectors/networkController', () => ({
...jest.requireActual('../../../../../selectors/networkController'),
selectEvmChainId: jest.fn(() => '0x1'), // Ethereum mainnet (not a testnet)
selectChainId: jest.fn(() => '0x1'), // BalanceEmptyState also needs this
}));

// Mock navigation hooks used by BalanceEmptyState
jest.mock('@react-navigation/native', () => ({
...jest.requireActual('@react-navigation/native'),
useNavigation: () => ({
navigate: jest.fn(),
goBack: jest.fn(),
reset: jest.fn(),
}),
}));

// Mock metrics hook used by BalanceEmptyState
jest.mock('../../../../../components/hooks/useMetrics', () => ({
useMetrics: () => ({
trackEvent: jest.fn(),
createEventBuilder: jest.fn(() => ({ record: jest.fn() })),
}),
}));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding necessary mocks for BalanceEmptyState component

Comment on lines +88 to +118
it('renders empty state when account group balance is zero', () => {
const {
selectAccountGroupBalanceForEmptyState,
selectBalanceBySelectedAccountGroup,
} = jest.requireMock('../../../../../selectors/assets/balances');

// Mock the regular balance selector to return data (prevents skeleton loader)
(selectBalanceBySelectedAccountGroup as jest.Mock).mockImplementation(
() => ({
totalBalanceInUserCurrency: 100, // Some non-zero amount for current network
userCurrency: 'usd',
}),
);

// Mock the empty state selector to return zero balance across all mainnet networks
(selectAccountGroupBalanceForEmptyState as jest.Mock).mockImplementation(
() => ({
totalBalanceInUserCurrency: 0, // Zero across all mainnet networks
userCurrency: 'usd',
}),
);

const { getByTestId } = renderWithProvider(<AccountGroupBalance />, {
state: testState,
});

const el = getByTestId(
WalletViewSelectorsIDs.BALANCE_EMPTY_STATE_CONTAINER,
);
expect(el).toBeOnTheScreen();
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding test to check for balance emtpy state

Comment on lines +10 to +14
selectAccountGroupBalanceForEmptyState,
} from '../../../../../selectors/assets/balances';
import { selectHomepageRedesignV1Enabled } from '../../../../../selectors/featureFlagController/homepage';
import { selectEvmChainId } from '../../../../../selectors/networkController';
import { TEST_NETWORK_IDS } from '../../../../../constants/network';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Importing new selector to calculate account group balance across all main nets, the feature flag for the new homepage redesign which includes full page scroll, the selector for current evm network and test networks ids for empty state display logic

import { Skeleton } from '../../../../../component-library/components/Skeleton';
import { useFormatters } from '../../../../hooks/useFormatters';
import AccountGroupBalanceChange from '../../components/BalanceChange/AccountGroupBalanceChange';
import BalanceEmptyState from '../../../BalanceEmptyState';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Importing BalanceEmptyState ui component

Comment on lines +31 to +33
const accountGroupBalance = useSelector(
selectAccountGroupBalanceForEmptyState,
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting account group balance across all chains using new selector

Comment on lines +53 to +64
// Check if account group balance (across all mainnet networks) is zero for empty state
const hasZeroAccountGroupBalance =
accountGroupBalance && accountGroupBalance.totalBalanceInUserCurrency === 0;

// Check if current network is a testnet
const isCurrentNetworkTestnet = TEST_NETWORK_IDS.includes(selectedChainId);

// Show empty state on accounts with an aggregated mainnet balance of zero
const shouldShowEmptyState =
hasZeroAccountGroupBalance &&
isHomepageRedesignV1Enabled &&
!isCurrentNetworkTestnet;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic for displaying Balance empty state.

  • Show if the account group has a zero balance across all main nets and isHomepageRedesignV1Enabled feature flag is enabled
  • Do not show if it's a test network regardless of balance - show $0.00

});
});

describe('selectAccountGroupBalanceForEmptyState', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding tests for new selector

Comment on lines +17 to +24
import { NON_EVM_TESTNET_IDS } from '@metamask/multichain-network-controller';
import {
parseCaipChainId,
CaipChainId,
KnownCaipNamespace,
} from '@metamask/utils';
import { toHex } from '@metamask/controller-utils';
import { TEST_NETWORK_IDS } from '../../constants/network';
Copy link
Contributor Author

@georgewrmarshall georgewrmarshall Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import helpful consts and utils for new selectAccountGroupBalanceForEmptyState selector

@sonarqubecloud
Copy link

@georgewrmarshall georgewrmarshall marked this pull request as ready for review October 31, 2025 05:33
accountGroupBalance && accountGroupBalance.totalBalanceInUserCurrency === 0;

// Check if current network is a testnet
const isCurrentNetworkTestnet = TEST_NETWORK_IDS.includes(selectedChainId);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Bug

The testnet detection logic in AccountGroupBalance uses EVM-specific selectors (selectEvmChainId and TEST_NETWORK_IDS). This incorrectly identifies non-EVM testnets (e.g., Solana) as mainnet, causing the empty state to display instead of the numerical balance ($0.00).

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently only show EVM test networks but this is a good point. If we were to add solana test net we may see the empty state. Do we have a non EVM test network constant that we can use here. Do you know @salimtb?

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Member

@cortisiko cortisiko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! 🌮 🌮

@georgewrmarshall georgewrmarshall added QA Passed QA testing has been completed and passed and removed needs-qa Any New Features that needs a full manual QA prior to being added to a release. labels Oct 31, 2025
@georgewrmarshall georgewrmarshall added this pull request to the merge queue Oct 31, 2025
Merged via the queue into main with commit 91c23b3 Oct 31, 2025
100 of 101 checks passed
@georgewrmarshall georgewrmarshall deleted the feat/balance-empty-state branch October 31, 2025 15:59
@github-actions github-actions bot locked and limited conversation to collaborators Oct 31, 2025
@metamaskbot metamaskbot added the release-7.59.0 Issue or pull request that will be included in release 7.59.0 label Oct 31, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

QA Passed QA testing has been completed and passed release-7.59.0 Issue or pull request that will be included in release 7.59.0 size-L team-design-system All issues relating to design system in Mobile

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants